package org.truenewx.tnxjee.webmvc.menu;

import java.util.*;
import jakarta.servlet.http.HttpServletRequest;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.truenewx.tnxjee.core.boot.ApplicationContextRunner;
import org.truenewx.tnxjee.core.util.CollectionUtil;
import org.truenewx.tnxjee.core.util.NetUtil;
import org.truenewx.tnxjee.model.spec.menu.Menu;
import org.truenewx.tnxjee.model.spec.menu.MenuItem;
import org.truenewx.tnxjee.model.spec.user.security.UserConfigAuthority;
import org.truenewx.tnxjee.service.security.access.GrantedAuthorityDecider;
import org.truenewx.tnxjee.web.context.SpringWebContext;
import org.truenewx.tnxjee.web.util.WebUtil;

@Component
public class MenuResolverImpl implements MenuResolver, ApplicationContextRunner {

    @Autowired
    private MenuTemplateLoader templateLoader;
    @Autowired
    private GrantedAuthorityDecider authorityDecider;
    private final Map<String, Menu> menus = new HashMap<>();

    @Override
    public void run(ApplicationContext context) throws Exception {
        List<Menu> menus = this.templateLoader.load();
        if (menus != null) {
            for (Menu menu : menus) {
                this.menus.put(menu.getName(), menu);
            }
        }
    }

    @Override
    public List<Menu> getFullMenus() {
        List<Menu> list = new ArrayList<>(this.menus.values());
        Collections.sort(list);
        return list;
    }

    @Override
    public Menu getFullMenu(String name) {
        if (StringUtils.isBlank(name)) {
            if (this.menus.size() == 1) {
                return CollectionUtil.getFirst(this.menus.values());
            }
            name = "default";
        }
        return this.menus.get(name);
    }

    @Override
    public Menu getGrantedMenu(String name, Collection<? extends GrantedAuthority> grantedAuthorities) {
        // 登录用户才可能具有可访问的菜单项，暂不支持匿名用户拥有可访问菜单项的情况
        if (CollectionUtils.isNotEmpty(grantedAuthorities)) {
            Menu fullMenu = getFullMenu(name);
            return filterGranted(fullMenu, grantedAuthorities);
        }
        return null;
    }

    @Override
    public List<Menu> getGrantedMenus(Collection<? extends GrantedAuthority> grantedAuthorities) {
        List<Menu> grantedMenus = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(grantedAuthorities)) {
            for (Menu menu : this.menus.values()) {
                Menu grantedMenu = filterGranted(menu, grantedAuthorities);
                if (grantedMenu != null && ArrayUtils.isNotEmpty(grantedMenu.getItems())) {
                    grantedMenus.add(grantedMenu);
                }
            }
        }
        Collections.sort(grantedMenus);
        return grantedMenus;
    }

    @Override
    public Menu filterGranted(Menu fullMenu, Collection<? extends GrantedAuthority> grantedAuthorities) {
        if (fullMenu != null) {
            Menu menu = fullMenu.cloneWithoutItems();
            MenuItem[] items = fullMenu.getItems();
            if (items != null) {
                boolean intranet = false;
                HttpServletRequest request = SpringWebContext.getRequest();
                if (request != null) {
                    intranet = NetUtil.isIntranetIp(WebUtil.getRemoteAddress(request));
                }
                List<MenuItem> grantedItems = new ArrayList<>();
                for (MenuItem item : items) {
                    MenuItem grantedItem = resolveGrantedItem(grantedAuthorities, intranet, item);
                    if (grantedItem != null) {
                        grantedItems.add(grantedItem);
                    }
                }
                menu.setItems(grantedItems.toArray(new MenuItem[0]));
            }
            return menu;
        }
        return null;
    }

    private MenuItem resolveGrantedItem(Collection<? extends GrantedAuthority> grantedAuthorities, boolean intranet,
            MenuItem item) {
        MenuItem grantedItem = null;
        // 先判断自身的可访问性
        Collection<UserConfigAuthority> configAuthorities = item.getAuthorities();
        if (configAuthorities != null) {
            // 配置权限限定中任意一个匹配，则视为具有权限
            for (UserConfigAuthority configAuthority : configAuthorities) {
                if (this.authorityDecider.isGranted(grantedAuthorities, intranet, configAuthority)) {
                    grantedItem = item.cloneWithoutSubs();
                }
            }
        }
        // 再从下级菜单项中判断可访问性
        MenuItem[] subs = item.getSubs();
        if (ArrayUtils.isNotEmpty(subs)) {
            List<MenuItem> grantedSubs = new ArrayList<>();
            for (MenuItem sub : subs) {
                MenuItem grantedSub = resolveGrantedItem(grantedAuthorities, intranet, sub);
                if (grantedSub != null) {
                    grantedSubs.add(grantedSub);
                }
            }
            // 下级菜单项中有可访问的项，则当前菜单项也具有可访问性
            if (!grantedSubs.isEmpty()) {
                if (grantedItem == null) {
                    grantedItem = item.cloneWithoutSubs();
                }
                grantedItem.setSubs(grantedSubs.toArray(new MenuItem[0]));
            }
        }
        return grantedItem;
    }

}
